package com.wj.dubbo.service.crawler.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.joda.time.DateTime;
import org.joda.time.Months;
import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.wj.console.common.exception.BusinessException;
import com.wj.console.constant.Enums.RESULT_CODE;
import com.wj.console.rabbit.message.CrawlerSocialFundMessage;
import com.wj.dubbo.service.common.RedisBillLockHandler;
import com.wj.dubbo.service.common.lock.GenericBillIdentify;
import com.wj.dubbo.service.crawler.entity.CrawlerFundCompany;
import com.wj.dubbo.service.crawler.entity.CrawlerFundInfo;
import com.wj.dubbo.service.crawler.entity.CrawlerFundSummary;
import com.wj.dubbo.service.crawler.entity.CrawlerSfManager;
import com.wj.dubbo.service.crawler.entity.OmMemberMessage;
import com.wj.dubbo.service.crawler.service.ICrawlerFundCompanyService;
import com.wj.dubbo.service.crawler.service.ICrawlerFundInfoService;
import com.wj.dubbo.service.crawler.service.ICrawlerFundSummaryService;
import com.wj.dubbo.service.crawler.service.ICrawlerSfManagerService;
import com.wj.dubbo.utils.DesUtil;
import com.wj.dubbo.utils.JuheUtils;
import com.wj.dubbo.utils.UUIDUtils;
@Service
public class CrawlFundService {
	
	private final static Logger logger = LoggerFactory.getLogger(CrawlFundService.class);
	
	private static final String URL_DOMAIN = "http://www.bjgjj.gov.cn";
	private static final String URL_PREFIX = "http://www.bjgjj.gov.cn/wsyw/wscx/";
	private static final String URL_LOGIN = "http://www.bjgjj.gov.cn/wsyw/wscx/gjjcx-login.jsp";
	private static final String URL_LK = "http://www.bjgjj.gov.cn/wsyw/wscx/asdwqnasmdnams.jsp";
	private static final String URL_ORG = "http://www.bjgjj.gov.cn/wsyw/wscx/gjjcx-choice.jsp";
	
	private static final String KEY_PATH_TEMP = "java.io.tmpdir";
//	private static final String PATH_JS = "src/main/resources/static/js/Encrypt.js";
	private static final String PATH_JS = "static/js/Encrypt.js";
	
	private static ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
	private ThreadLocal<ScriptEngine> scriptEngine = null;
	
//	static {
//		ScriptEngine engine = scriptEngineManager.getEngineByName("javascript");
//		InputStream is = null;
//		InputStreamReader reader = null;
//		try{
//			/** 调用JS，进行账号密码加密 */
//		    is = CrawlFundService.class.getClassLoader().getResourceAsStream(PATH_JS);
//            reader = new InputStreamReader(is, "UTF-8");
//            engine.eval(reader);
//            scriptEngine.set(engine);
//		}catch (Exception e) {
//			logger.error("初始化JavaScript引擎失败", e);
//		}finally{
//			if(reader!=null){
//				try {
//					reader.close();
//				} catch (IOException e) {
//					
//				}
//			}
//			if(is!=null){
//				try {
//					is.close();
//				} catch (IOException e) {
//					
//				}
//			}
//		}
//		
//	}
	
	private ScriptEngine getScriptEngine(){
		ScriptEngine engine = scriptEngineManager.getEngineByName("javascript");
		InputStream is = null;
		InputStreamReader reader = null;
		try{
			/** 调用JS，进行账号密码加密 */
		    is = CrawlFundService.class.getClassLoader().getResourceAsStream(PATH_JS);
            reader = new InputStreamReader(is, "UTF-8");
            engine.eval(reader);
            
		}catch (Exception e) {
			logger.error("初始化JavaScript引擎失败", e);
		}finally{
			if(reader!=null){
				try {
					reader.close();
				} catch (IOException e) {
					
				}
			}
			if(is!=null){
				try {
					is.close();
				} catch (IOException e) {
					
				}
			}
		}
		return engine;
	}
	
	private ThreadLocal<ScriptEngine> getEngine(){
		if(null != scriptEngine){
			scriptEngine.set(getScriptEngine());
			return scriptEngine;
		}
		scriptEngine = new ThreadLocal<ScriptEngine>();
		scriptEngine.set(getScriptEngine());
		return scriptEngine;
	}
	
	@SuppressWarnings("serial")
	public static final Set<String> SET_BUSINESS_TYPE = new HashSet<String>() {
		{
			add("汇缴");
			add("补缴");
			add("年度结息");
			add("内部转移");
			add("提取");
		}
	};
	public static final String STR_BUSINESS_TYPE_HUIJIAO = "汇缴";

	@Autowired
	private RabbitTemplate rabbitTemplate;
	@Autowired
	private ICrawlerSfManagerService crawlerSfManagerService;
	@Autowired
	private ICrawlerFundCompanyService crawlerFundCompanyService;
	@Autowired
	private ICrawlerFundInfoService crawlerFundInfoService;
	@Autowired
	private ICrawlerFundSummaryService crawlerFundSummaryService;
	@Autowired
	private RedisBillLockHandler redisBillLockHandler;


	
	/**
	 * 爬取公积金数据
	 */
	@Transactional
	public CrawlerSocialFundMessage crawlFundData(CrawlerSocialFundMessage socialFundAccount) throws Exception{
		logger.info("爬取公积金数据开始：" + socialFundAccount);
		/** 请求 */
		socialFundAccount.setTp("2");
		if( StringUtils.isNotEmpty(socialFundAccount.getIdentityId()) || StringUtils.isNotEmpty(socialFundAccount.getJointCard()) ){
			
		}else{
			throw new BusinessException(RESULT_CODE.SYSTERM_MAINTAIN.key, "请求参数错误");
		}
		
		if(StringUtils.isEmpty(socialFundAccount.getPwd()) || StringUtils.isEmpty(socialFundAccount.getMemberId()) || StringUtils.isEmpty(socialFundAccount.getCityId())){
			throw new BusinessException(RESULT_CODE.SYSTERM_MAINTAIN.key, "请求参数错误");
		}
		/** 加锁 */
		String key = null;
		if(StringUtils.isNotEmpty(socialFundAccount.getIdentityId())){
			// 身份证号包含字母的需要转换大写！重要！
			socialFundAccount.setIdentityId(socialFundAccount.getIdentityId().toUpperCase());
			key = socialFundAccount.getIdentityId();
		}else{
			key = socialFundAccount.getJointCard();
		}
		logger.info("加锁开始：" + key);
		GenericBillIdentify billIdentify = new GenericBillIdentify("fund:" + key);
		
		try{
			if(!redisBillLockHandler.tryLock(billIdentify)){
				logger.info("加锁失败退出：" + billIdentify.uniqueIdentify());
				return null;
			}
			/** 访问登录页面 */
			Response resLogin = Jsoup.connect(URL_LOGIN).execute();
			/** 获取登录请求参数 */
			Map<String, String> loginParams = getLoginParams(socialFundAccount, resLogin);
			
			/** 登录并爬取数据 */
			String resultCode = doLoginAndCrawlData(socialFundAccount, resLogin, loginParams);
			if(resultCode.equals(RESULT_CODE.PASSWORD_ERROR.key)){
				throw new BusinessException(RESULT_CODE.PASSWORD_ERROR.key, RESULT_CODE.PASSWORD_ERROR.value);
			}
			logger.info("爬取公积金数据完成：" + socialFundAccount);
			return socialFundAccount;
		}finally{
			redisBillLockHandler.unLock(billIdentify);
		}
	}
	
	
	private String encrypt(String val) throws Exception{
		Invocable jsInvoke = (Invocable) getEngine().get();
		String encryptStr = null;
		try {
			encryptStr = String.valueOf( jsInvoke.invokeFunction("do_encrypt", val));
		} catch (NoSuchMethodException | ScriptException e) {
			logger.error("JavaScript加密错误", e);
			throw e;
		}
		return encryptStr;
	}
	
	/**
	 * 获取登录请求参数
	 */
	private Map<String, String> getLoginParams(CrawlerSocialFundMessage socialFundAccount, Response resLogin) {
		Map<String, String> loginParams = new HashMap<String, String>();
		try {
			Map<String, String> resLoginCookies = resLogin.cookies();
			
			/** 解析登录页面 */
			Document docLogin = resLogin.parse();
			String loginLK = docLogin.select("#lk").val();
			
			/** 验证码 */
			String verificationCode = getVerificationCode(resLogin);
			
			/** 登录密钥 */
	        Document docLoginLK = Jsoup.connect(URL_LK).cookies(resLoginCookies).post();
	        String loginLKContent = docLoginLK.select("body").first().text();
			if(loginLKContent != null && loginLKContent.length() > 4){
				loginLK = loginLKContent.substring(4);
			}
			//System.out.println("loginLK：" + loginLK);
			
			/** 登录账号类型：1 - 身份证，5 - 联名卡。 */
			String loginAccountType = "1";
			String loginAccount = socialFundAccount.getIdentityId();
			if(StringUtils.isEmpty(socialFundAccount.getIdentityId())){
				loginAccountType = "5";
				loginAccount = socialFundAccount.getJointCard();
			}
			
			loginAccount = encrypt(loginAccount);
			// 解密
			String pwd = socialFundAccount.getPwd();
			String loginPwd = encrypt(DesUtil.decrypt(pwd, DesUtil.KEY_CRAWLER));

			/** 登录请求参数 */
			loginParams.put("lb", loginAccountType);// 登录账号类型
			loginParams.put("bh", loginAccount);// 登录账号
			loginParams.put("mm", loginPwd);// 登录密码
			loginParams.put("gjjcxjjmyhpppp", verificationCode);//验证码
			loginParams.put("lk", loginLK);
			
		} catch (Exception e) {
			logger.error("获取登录参数错误", e);
		} 
		return loginParams;
	}


	/**
	 * 获取验证码
	 */
	private String getVerificationCode(Response resLogin) {
		String verificationCode = null;
		FileOutputStream fosImg = null;
		File fileLoginImg = null;
		try {
			Map<String, String> resLoginCookies = resLogin.cookies();
			
			/** 解析登录页面 */
			Document docLogin = resLogin.parse();
			String loginImgSrc = docLogin.select("#login_tab_2 img").attr("src");
			
			// 验证码图片
			Response resLoginImg = Jsoup.connect(URL_DOMAIN + loginImgSrc).cookies(resLoginCookies).ignoreContentType(true).execute();
			// C:\Users\ADMINI~1\AppData\Local\Temp\
			fileLoginImg = new File(System.getProperty(KEY_PATH_TEMP) + UUIDUtils.getUUIDStr() + ".png");
			fosImg = new FileOutputStream(fileLoginImg);
			fosImg.write(resLoginImg.bodyAsBytes());
			
	         // 解析验证码
    		String jsonStr = JuheUtils.post("8001", fileLoginImg);
    		logger.info("聚合返回数据：" + jsonStr);
    		JSONObject jsonObj = JSONObject.parseObject(jsonStr);
    		if(null != jsonObj){    			
    			verificationCode = (String) jsonObj.get("result");
    		}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			if(fileLoginImg!=null && fileLoginImg.isFile()){
				fileLoginImg.delete();
			}
			if(fosImg!=null){
				try {
					fosImg.close();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
			}
		}
		if(StringUtils.isBlank(verificationCode)){
			verificationCode = "";
		}
		logger.info("验证码：" + verificationCode);
		return verificationCode;
	}

	
	/**
	 * 登录并爬取公积金数据
	 */
	private String doLoginAndCrawlData(CrawlerSocialFundMessage socialFundAccount, Response resLogin, Map<String, String> loginParams) throws IOException {
		
		Map<String, String> resLoginCookies = resLogin.cookies();
		
		Map<String, Object> returnMap = null;
		// 如果验证码错误，有3次机会。
		for (int t = 1; t <= 3; t++) {
			logger.info(String.format("%s登录尝试：%s 次", socialFundAccount, t));
			/** 登录 */
			returnMap = doLogin(resLoginCookies, loginParams);
			String resultCode = (String) returnMap.get("resultCode");
			// 验证码错误
			if(resultCode.equals(RESULT_CODE.VERIFICATION_CODE_ERROR.key)){
				logger.info("验证码错误...");
				// 重新获取验证码
				loginParams.put("gjjcxjjmyhpppp", getVerificationCode(resLogin));
				continue;
			}else{
				break;
			}
		}
		
		// 账户表ID
		String managerId = socialFundAccount.getId();
		
		String resultCode = (String) returnMap.get("resultCode");
		// 登录成功
		if(resultCode.equals(RESULT_CODE.SUCCESS.key)){
			logger.info("登录成功...");
			
			// 有效
			socialFundAccount.setStatus("1");
			// 登录
			if(!socialFundAccount.getIsBatch()){
				socialFundAccount.setLoginStatus("1");
			}
			
			/** 当使用联名卡时，关联身份证号码 **/
			if(StringUtils.isEmpty(socialFundAccount.getIdentityId())){
				// 个人登记号
				String personalRegisterNo = getPersonalRegisterNo((Document)returnMap.get("docOrg"), resLoginCookies);
				if(StringUtils.isEmpty(personalRegisterNo)){
					throw new BusinessException(RESULT_CODE.SYSTERM_MAINTAIN.key, "个人登记号无法获取！"); 
				}else{
					String idNo = personalRegisterNo.substring(0, personalRegisterNo.length() - 2);
					// 身份证号包含字母的需要转换大写！重要！
					socialFundAccount.setIdentityId(idNo.toUpperCase());
				}
			}
			
			if(StringUtils.isEmpty(managerId)){
				Wrapper<CrawlerSfManager> queryCrawlerSfManager = new EntityWrapper<CrawlerSfManager>();
				queryCrawlerSfManager.eq("identity_id", socialFundAccount.getIdentityId());
				queryCrawlerSfManager.eq("tp", "2");
				CrawlerSfManager csm = crawlerSfManagerService.selectOne(queryCrawlerSfManager);
				if(csm != null){
					managerId = csm.getId();
					socialFundAccount.setId(managerId);
				}
				if(StringUtils.isEmpty(managerId)){
					managerId = UUIDUtils.getUUIDStr();
				}
			}
			
			/** 解析公积金开户单位列表 */
			List<CrawlerFundCompany> fundOrgList = new ArrayList<CrawlerFundCompany>();
			parseFundOrgData(fundOrgList, (Document)returnMap.get("docOrg"));
			if(fundOrgList.size() > 0){
				/** 过滤即将要爬取的开户单位列表 */
				List<CrawlerFundCompany> fundOrgCrawlList = new ArrayList<CrawlerFundCompany>();
				this.getFundOrgCrawlList(fundOrgCrawlList, fundOrgList, socialFundAccount);
				
				if(fundOrgCrawlList.size() > 0){
					/** 根据开户单位列表，爬取公积金信息 */
					CrawlerFundInfo fundInfo = new CrawlerFundInfo();
					List<CrawlerFundSummary> fundSummaryList = new ArrayList<CrawlerFundSummary>();
					
					/** 爬取 */
					crawlFundInfoData(fundInfo, fundSummaryList, resLoginCookies, fundOrgCrawlList);
					
					/** 处理爬取的信息 */
					processFundInfoData(fundInfo, fundSummaryList, fundOrgCrawlList, socialFundAccount, managerId);
				}
			}
			
			// 操作结束，无需使用ID来标记是插入还是更新了，因此设置ID值
			socialFundAccount.setId(managerId);
		}

		if(resultCode.equals(RESULT_CODE.PASSWORD_ERROR.key) || resultCode.equals(RESULT_CODE.VERIFICATION_CODE_ERROR.key)){
			logger.info("登录失败...");
			// 无效
			socialFundAccount.setStatus("0");
			// 注销
			socialFundAccount.setLoginStatus("0");
		}
		
		// TODO 如果成功，且非跑批，更新账户表其他记录变为注销。
		if(resultCode.equals(RESULT_CODE.SUCCESS.key) && !socialFundAccount.getIsBatch()){
			Wrapper<CrawlerSfManager> updateCrawlerSfManager = new EntityWrapper<CrawlerSfManager>();
			updateCrawlerSfManager.eq("member_id", socialFundAccount.getMemberId());
			CrawlerSfManager updateManager = new CrawlerSfManager();
			updateManager.setLoginStatus("0");
			crawlerSfManagerService.update(updateManager, updateCrawlerSfManager);
		}
		
		// TODO 新增或更新账户表
		if(StringUtils.isNotEmpty(socialFundAccount.getId())){
			CrawlerSfManager manager = new CrawlerSfManager();
			BeanUtils.copyProperties(socialFundAccount, manager);
			Wrapper<CrawlerSfManager> queryCrawlerSfManager = new EntityWrapper<CrawlerSfManager>();
			queryCrawlerSfManager.eq("id", managerId);
			List<Object> crawlerSfManagerList = crawlerSfManagerService.selectObjs(queryCrawlerSfManager);
			if(crawlerSfManagerList.size() == 0){
				Date nowDate = new Date();
				manager.setCreateTime(new Date());
				crawlerSfManagerService.insert(manager);
				// 用于返回参数
				socialFundAccount.setCreateTime(nowDate);
			}else{
				Wrapper<CrawlerSfManager> updateCrawlerSfManager = new EntityWrapper<CrawlerSfManager>();
				updateCrawlerSfManager.eq("id", managerId);
				crawlerSfManagerService.update(manager, updateCrawlerSfManager);
			}
		}

		
		// 推送消息
		if(resultCode.equals(RESULT_CODE.SUCCESS.key) && socialFundAccount.getIsPush()){
			try {
				logger.info("公积金推送消息开始...");
				OmMemberMessage message = new OmMemberMessage();
				message.setMemberId(socialFundAccount.getMemberId());
				message.setSummary("您的公积金信息已更新完毕，请查阅。");
				message.setMessageType("crawlFund");
				MessageProperties prop = new MessageProperties();
				prop.setContentType(MessageProperties.CONTENT_TYPE_BYTES);
				Message msg = new Message(JSON.toJSONString(message).getBytes(), prop);
				rabbitTemplate.send("message_push_queue", msg);
				logger.info("公积金推送消息结束...");
			} catch (Exception e) {
				logger.error("公积金推送消息错误...", e);
			}
		}
		return resultCode;
	}


	/**
	 * 登录
	 */
	private Map<String, Object> doLogin(Map<String, String> resLoginCookies, Map<String, String> loginParams) {
		Map<String, Object> returnMap = new HashMap<String, Object>();
		String resultCode = RESULT_CODE.PASSWORD_ERROR.key;
		Document docOrg = null;
		try {
			// 登录
			docOrg = Jsoup.connect(URL_ORG).cookies(resLoginCookies).data(loginParams).followRedirects(true).post();
			// 如果登录成功，则含有以下内容：<span>请选择相应开户单位</span>"，此规则不严谨。
			//Elements spans = docOrg.select("body > table:nth-child(3) > tbody > tr:nth-child(3) > td > table > tbody > tr > td > div > table > tbody > tr:nth-child(1) > td > div > span");
			
			// 登录失败
			//"window.location='gjjcx-logineoor.jsp"
			
			// 登录失败
			// 对不起，您当前的查询密码安全级别过低，为了保证您的个人信息安全，请输入联名卡号进行登录，给您带来的不便请您谅解！
			
			String data = docOrg.html();
			int locationIndex = data.indexOf("window.location=");
			// 登录成功
			if(locationIndex==-1){//没有跳转
				resultCode = RESULT_CODE.SUCCESS.key;
			}else{
				int pwdIndex = data.indexOf("对不起，您当前的查询密码安全级别过低");
				if(pwdIndex != -1){// 匹配
					resultCode = RESULT_CODE.PASSWORD_ERROR.key;
				}else{// 不匹配
					int index = data.indexOf("window.location='gjj_cx.jsp?nicam=");
					if(index != -1){
						resultCode = RESULT_CODE.SUCCESS.key;
					}else{//登录失败
						// 如果登录失败：docOrg.html() ：alert("校验码错误");window.location='gjjcx-login.jsp';
						int existIndex = data.indexOf("校验码错误");
						if(existIndex!=-1){
							resultCode = RESULT_CODE.VERIFICATION_CODE_ERROR.key;
						}
					}
				}
			}
			returnMap.put("resultCode", resultCode);
			returnMap.put("docOrg", docOrg);
		} catch(Exception e){
			logger.error("登录请求错误", e);
			if(null != docOrg){				
				logger.info("错误页面：" + docOrg.html());
			}
		}
		return returnMap;
	}

	/**
	 * 获取个人登记号
	 */
	private String getPersonalRegisterNo(Document docOrg, Map<String, String> resLoginCookies) throws IOException {
		// 第一个开户单位
		Elements tables = docOrg.select("#new-mytable");
		Element tr = tables.get(0).select("tr").get(1);
		Elements tds = tr.getElementsByTag("td");
		Element td = tds.get(1);
		Elements as = td.getElementsByTag("a");
		Element a = as.get(0);
		String onclick = a.attr("onclick");
		String queryUrl = onclick.substring(onclick.indexOf("(") + 1, onclick.indexOf(","));
		queryUrl = queryUrl.replaceAll("&quot;", "");
		queryUrl = queryUrl.replaceAll("\"", "");
		
		// 访问页面并获取
		Document docFundInfo = Jsoup.connect(URL_PREFIX + queryUrl).cookies(resLoginCookies).data(new HashMap<String, String>()).followRedirects(true).post();
		String personalRegisterNo = docFundInfo.select("#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(1) > td:nth-child(4)").text();
		return personalRegisterNo;
	}


	/**
	 * 解析公积金开户单位列表
	 */
	private void parseFundOrgData(List<CrawlerFundCompany> fundOrgList, Document docOrg) {
		Elements tables = docOrg.select("#new-mytable");
		if(tables!=null && tables.size() > 0){
			Elements trs = tables.get(0).select("tr");
			for (Element tr : trs) {
				Elements tds = tr.getElementsByTag("td");
				if(tds!=null && tds.size() > 0){
					CrawlerFundCompany fc = new CrawlerFundCompany();
					for (int i = 0; i < tds.size(); i++) {
						Element td = tds.get(i);
						if(td!=null){
							String text = td.text();
							if(i==0){
								fc.setCompanyNo(text);
							}else if(i==1){
								Elements as = td.getElementsByTag("a");
								Element a = as.get(0);
								String onclick = a.attr("onclick");
								String queryUrl = onclick.substring(onclick.indexOf("(") + 1, onclick.indexOf(","));
								queryUrl = queryUrl.replaceAll("&quot;", "");
								queryUrl = queryUrl.replaceAll("\"", "");
								
								fc.setCompanyName(text);
								fc.setQueryUrl(queryUrl);
							}
						}
					}// td循环结束
					fundOrgList.add(fc);
				}
			}// tr循环结束
		}
	}


	/**
	 * 爬取公积金信息
	 */
	private void crawlFundInfoData(CrawlerFundInfo fundInfo, List<CrawlerFundSummary> fundSummaryList,
			Map<String, String> resLoginCookies, List<CrawlerFundCompany> fundOrgList) {
		// TODO 依赖于列表正序还是倒序，正序。
		int fundInfoIndex = fundOrgList.size() - 1;
		for (int i = 0; i < fundOrgList.size(); i++) {
			CrawlerFundCompany crawlerFundCompany = fundOrgList.get(i);
			String queryUrl = crawlerFundCompany.getQueryUrl();
			try {
				Document docFundInfo = Jsoup.connect(URL_PREFIX + queryUrl).cookies(resLoginCookies).data(new HashMap<String, String>()).followRedirects(true).post();
//				System.out.println("住房公积金页面信息：");
//				System.out.println(crawlerFundCompany.getCompanyName());
//				System.out.println(queryUrl);
//				System.out.println(docFundInfo.html());
				/** 解析公积金信息 */
				if(i == fundInfoIndex){
					parseFundInfoData(fundInfo, fundSummaryList, resLoginCookies, docFundInfo);
				}else{
					parseFundInfoData(null, fundSummaryList, resLoginCookies, docFundInfo);
				}
			} catch (IOException e) {
				logger.error(e.getMessage(), e);
			}
		}
	}


	/**
	 * 解析公积金信息
	 * @param resLoginCookies 
	 */
	private void parseFundInfoData(CrawlerFundInfo fundInfo, List<CrawlerFundSummary> fundSummaryList,
			Map<String, String> resLoginCookies, Document docFundInfo) {
		/** 住房公积金个人总账信息 */
		String fundCompanyNo = docFundInfo.select("#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(3) > td:nth-child(2)").text();
		if(fundInfo!=null){
//			System.out.println("住房公积金个人总账信息");
			
			String name = docFundInfo.select("#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(1) > td:nth-child(2)").text();
			String customerNo = docFundInfo.select("#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(1) > td:nth-child(4)").text();
			String fundCompany = docFundInfo.select("#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(3) > td:nth-child(4)").text();
			String payCenter = docFundInfo.select("#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(4) > td:nth-child(4)").text();
			String balanceAmountStr = docFundInfo.select("#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(5) > td:nth-child(2) > div").text();
			fundInfo.setName(name);
			fundInfo.setCustomerNo(getStr(customerNo));
			fundInfo.setFundCompany(getStr(fundCompany));
			fundInfo.setPayCenter(getStr(payCenter));
			fundInfo.setBalanceAmount(getAmount(getStr(balanceAmountStr)));
			
			// 姓名
//			"#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(1) > td:nth-child(2)"
			// 个人登记号
//			"#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(1) > td:nth-child(4)"
			// 单位登记号
//			"#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(3) > td:nth-child(2)"
			// 单位名称
//			"#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(3) > td:nth-child(4)"
			// 所属管理部名称
//			"#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(4) > td:nth-child(4)"
			// 当前余额
//			"#t1Contents > div:nth-child(2) > table > tbody > tr:nth-child(5) > td:nth-child(2) > div"

		}
		
		/** 住房公积金个人明细账信息 */
		if(fundSummaryList!=null){
//			System.out.println("住房公积金个人明细账信息");
			parseFundListData(fundSummaryList, docFundInfo, "#tab-style", fundCompanyNo);
			
			/** 查看历史明细账信息 */
			Elements as = docFundInfo.select("#t3Contents > div > div:nth-child(4) > span > a");
			if(as!=null && as.size() > 0){
				Element a = as.get(0);
				String onclick = a.attr("onclick");
				String queryUrl = onclick.substring(onclick.indexOf("(") + 1, onclick.indexOf(","));
				queryUrl = queryUrl.replaceAll("&quot;", "");
				queryUrl = queryUrl.replaceAll("\"", "");
				queryUrl = queryUrl.replaceAll("'", "");
				try {
					Document docFundInfoHistory = Jsoup.connect(URL_PREFIX + queryUrl).cookies(resLoginCookies).data(new HashMap<String, String>()).followRedirects(true).post();
					parseFundListData(fundSummaryList, docFundInfoHistory, "#new-mytable3", fundCompanyNo);
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
				}
			}
		}
	}


	/**
	 * 解析公积金个人明细账信息 或  历史明细账信息
	 */
	private void parseFundListData(List<CrawlerFundSummary> fundSummaryList, Document docFund, String tableId, String fundCompanyNo) {
		Elements tables = docFund.select(tableId);
		if(tables!=null && tables.size() > 0){
			Elements trs = tables.get(0).select("tr");
			for (Element tr : trs) {
				Elements tds = tr.getElementsByTag("td");
				if(tds!=null && tds.size() > 0){
					CrawlerFundSummary fs = new CrawlerFundSummary();
					for (int i = 0; i < tds.size(); i++) {
						Element td = tds.get(i);
						if(td!=null){
							String text = getStr(td.text());
							if(StringUtils.isNotBlank(text)){
								// 到账日期
								if(i==0){
									fs.setArrivalDate(getNumberStr(text));
								}else if(i==1){ // 汇补缴年月
									fs.setPayYearMonth(getNumberStr(text));
								}else if(i==2){ // 业务类型
									fs.setBusinessType(text);
								}else if(i==3){ // 增加额(元)
									fs.setAmountAdd(getAmountStr(text));
								}else if(i==4){ // 减少额(元)，正数，下面相加时转负数。
									fs.setAmountMinus(getAmountStr(text));
								}
							}
						}
					}// td循环结束
					
					// 设置开户登记号
					if(StringUtils.isNotBlank(fundCompanyNo)){
						fs.setFundCompanyNo(fundCompanyNo);
					}
					
					// 设置金额，增加额 + 减少额
					fs.setIncreaseAmount(getIncreaseAmount(fs.getAmountAdd(), fs.getAmountMinus()));
					
					// 过滤业务类型
					//if(SET_BUSINESS_TYPE.contains(fs.getBusinessType())){
						fundSummaryList.add(fs);
					//}
				}
			}// tr循环结束
		}
	}

	private String getStr(String text) {
		if(StringUtils.isBlank(text)){
			return text;
		}else{
			text = StringUtils.replace(text, "  ", "");
			return StringUtils.deleteWhitespace(text);
		}
		
	}
	private String getNumberStr(String text) {
		return text.replaceAll("\\D+", "");
	}
	private BigDecimal getAmount(String text) {
		text = text.replaceAll("[^0-9.]", "");
		if(StringUtils.isEmpty(text)){
			text="0.00";
		}
		return new BigDecimal(text);
	}
	private String getAmountStr(String text) {
		text = text.replaceAll("[^0-9.]", "");
		if(StringUtils.isEmpty(text)){
			return "0.00";
		}
		return text;
	}
	private BigDecimal getIncreaseAmount(String amountAdd, String amountMinus) {
		BigDecimal amountAddBd = getAmount(amountAdd);
		BigDecimal amountMinusBd = getAmount(amountMinus);
		if(amountMinusBd.compareTo(BigDecimal.ZERO) > 0){
			amountMinusBd = amountMinusBd.multiply(new BigDecimal("-1"));
		}
		return amountAddBd.add(amountMinusBd);
	}


	
	/**
	 * 过滤即将要爬取的开户单位列表 
	 */
	private void getFundOrgCrawlList(List<CrawlerFundCompany> fundOrgCrawlList, List<CrawlerFundCompany> fundOrgList, CrawlerSocialFundMessage socialFundAccount) {
		String managerId = socialFundAccount.getId();
		if(StringUtils.isNotBlank(managerId)){
			// 查询数据库已有开户列表数据
			Wrapper<CrawlerFundCompany> queryFundCompany = new EntityWrapper<CrawlerFundCompany>();
			queryFundCompany.eq("manager_id", managerId);
			queryFundCompany.orderBy("create_time", true);// 升序
			List<CrawlerFundCompany> cList = crawlerFundCompanyService.selectList(queryFundCompany);
			
			// 取数据库最新一条数据，标记最终不插入数据库。
			String noInsert = null;
			// 取数据库其余数据的补集，标记最终插入数据库。
			Set<String> insertSet = new HashSet<String>();
			
			// TODO 依赖于列表正序还是倒序，目前正序。
			int size = cList.size();
			if(size > 0){
				int begin = 0;
				int end = size - 1;
				noInsert = cList.get(end).getCompanyNo();
				for (int i = begin; i < end; i++) {
					insertSet.add(cList.get(i).getCompanyNo());
				}
			}
			
			// 遍历爬取的数据
			for (CrawlerFundCompany org : fundOrgList) {
				String companyNo = org.getCompanyNo();
				if(StringUtils.isNotEmpty(companyNo)){
					// 取数据库最新一条数据，标记最终不插入数据库。
					if(StringUtils.equals(companyNo, noInsert)){
						org.setDoInsert(false);
						fundOrgCrawlList.add(org);
					}else{// 取数据库其余数据的补集，标记最终插入数据库。
						if(insertSet.size() > 0){// 过滤
							if(!insertSet.contains(companyNo)){
								org.setDoInsert(true);
								fundOrgCrawlList.add(org);
							}
						}else{//无需过滤
							org.setDoInsert(true);
							fundOrgCrawlList.add(org);
						}
					}
				}
			}
		}else{
			// 遍历爬取的数据
			for (CrawlerFundCompany org : fundOrgList) {
				org.setDoInsert(true);
			}
			fundOrgCrawlList.addAll(fundOrgList);
		}
	}
	
	/**
	 * 处理爬取的信息
	 */
	private void processFundInfoData(CrawlerFundInfo fundInfo, List<CrawlerFundSummary> fundSummaryList, List<CrawlerFundCompany> fundOrgCrawlList, CrawlerSocialFundMessage socialFundAccount, String managerId) {
		// 账户表姓名
		if(StringUtils.isNotBlank(fundInfo.getName())){
			socialFundAccount.setName(fundInfo.getName());
		}
		
		// 公积金基础信息表
		Date nowDate = new Date();
		fundInfo.setManagerId(managerId);
		fundInfo.setCreateTime(nowDate);
		fundInfo.setUpdateTime(nowDate);
		
		// 过滤条件：到账日期
		String arrivalDate = null;
		Wrapper<CrawlerFundSummary> queryFundSummary = new EntityWrapper<CrawlerFundSummary>();
		queryFundSummary.eq("manager_id", managerId);
		queryFundSummary.orderBy("arrival_date", false);
		Page<CrawlerFundSummary> queryFundSummaryPage = new Page<CrawlerFundSummary>();
		queryFundSummaryPage.setSize(1);
		queryFundSummaryPage = crawlerFundSummaryService.selectPage(queryFundSummaryPage , queryFundSummary);
		List<CrawlerFundSummary> summaryList = queryFundSummaryPage.getRecords();
		if(summaryList.size() > 0){
			arrivalDate = summaryList.get(0).getArrivalDate();
		}
		
		// 公积金汇总列表
		List<CrawlerFundSummary> fundSummarySaveList = new ArrayList<CrawlerFundSummary>();
		if(fundSummaryList.size() > 0){
			// 过滤
			if(StringUtils.isNotEmpty(arrivalDate)){
				for (CrawlerFundSummary crawlerFundSummary : fundSummaryList) {
					crawlerFundSummary.setId(UUIDUtils.getUUIDStr());
					crawlerFundSummary.setManagerId(managerId);
					crawlerFundSummary.setCreateTime(nowDate);
					// 取大于数据库到账日期
					if(StringUtils.isNotEmpty(crawlerFundSummary.getArrivalDate()) && crawlerFundSummary.getArrivalDate().compareTo(arrivalDate) > 0){
						fundSummarySaveList.add(crawlerFundSummary);
					}
				}
			}else{// 无需过滤
				for (CrawlerFundSummary crawlerFundSummary : fundSummaryList) {
					crawlerFundSummary.setId(UUIDUtils.getUUIDStr());
					crawlerFundSummary.setManagerId(managerId);
					crawlerFundSummary.setCreateTime(nowDate);
					fundSummarySaveList.add(crawlerFundSummary);
				}
			}
		}
		/** TODO 保存 */
		if(fundSummarySaveList.size() > 0){
			crawlerFundSummaryService.insertBatch(fundSummarySaveList);
		}
		
		/** 计算最近缴纳年月、连续缴纳月个数 **/
		calculatePayYearMonth(fundInfo);

		
		/** TODO 如果不存在则插入，存在则更新。 */
		Wrapper<CrawlerFundInfo> queryFundInfo = new EntityWrapper<CrawlerFundInfo>();
		queryFundInfo.eq("manager_id", managerId);
		List<Object> crawlerFundInfoList = crawlerFundInfoService.selectObjs(queryFundInfo);
		if(crawlerFundInfoList.size() == 0){
			fundInfo.setId(UUIDUtils.getUUIDStr());
			crawlerFundInfoService.insert(fundInfo);
		}else{
			Wrapper<CrawlerFundInfo> updateFundInfo = new EntityWrapper<CrawlerFundInfo>();
			updateFundInfo.eq("manager_id", managerId);
			crawlerFundInfoService.update(fundInfo, updateFundInfo);
		}
		
		
		// 公积金开户单位表
		List<CrawlerFundCompany> fundOrgSaveList = new ArrayList<CrawlerFundCompany>();
		if(fundOrgCrawlList.size() > 0){
			for (CrawlerFundCompany crawlerFundCompany : fundOrgCrawlList) {
				if(crawlerFundCompany.isDoInsert()){
					crawlerFundCompany.setId(UUIDUtils.getUUIDStr());
					crawlerFundCompany.setManagerId(managerId);
					// TODO 依赖于列表正序还是倒序，目前正序。
					nowDate = DateUtils.addSeconds(nowDate, 10);
					crawlerFundCompany.setCreateTime(nowDate);
					fundOrgSaveList.add(crawlerFundCompany);
				}
			}
		}
		/** TODO 保存 */
		if(fundOrgSaveList.size() > 0){
			crawlerFundCompanyService.insertBatch(fundOrgSaveList);
		}
	}


	/**
	 * 计算最近缴纳年月、连续缴纳月个数
	 */
	private void calculatePayYearMonth(CrawlerFundInfo fundInfo) {
		// 最近缴纳年月，格式yyyyMM
		String payYearMonth = null;
	    // 连续缴纳月个数
		Integer payMonthCount = 0;
		
		// 查询缴纳年月不为NULL，且为指定类型的，按缴纳年月倒序排列。
		Wrapper<CrawlerFundSummary> query4PayYearMonth = new EntityWrapper<CrawlerFundSummary>();
		query4PayYearMonth.eq("manager_id", fundInfo.getManagerId());
		query4PayYearMonth.eq("business_type", STR_BUSINESS_TYPE_HUIJIAO);
		query4PayYearMonth.where("pay_year_month is not null");
		query4PayYearMonth.orderBy("pay_year_month", false);
		List<CrawlerFundSummary> fundSummaryListPayYearMonth = crawlerFundSummaryService.selectList(query4PayYearMonth );
		
		DateFormat yyyyMM = new SimpleDateFormat("yyyyMM");
		if(fundSummaryListPayYearMonth.size() > 0){
			int firstIndex = 0;
			payYearMonth = fundSummaryListPayYearMonth.get(firstIndex).getPayYearMonth();
			payMonthCount = 1;
			// 上一个缴纳年月
			String payYearMonthLast = payYearMonth;
			for (int i = firstIndex + 1; i < fundSummaryListPayYearMonth.size(); i++) {
				String payYearMonthCurrent = fundSummaryListPayYearMonth.get(i).getPayYearMonth();
				DateTime b1 = null;
				DateTime s2 = null;
				try {
					b1 = new DateTime(yyyyMM.parse(payYearMonthLast));
					s2 = new DateTime(yyyyMM.parse(payYearMonthCurrent));
				} catch (ParseException e) {
					logger.error(e.getMessage(), e);
					continue;
				}
				int diff = Months.monthsBetween(s2, b1).getMonths();
				if(diff > 1){
					break;
				}else{
					payMonthCount++;
					payYearMonthLast = payYearMonthCurrent;
				}
			}
		}

		// 设置
		if(StringUtils.isNotEmpty(payYearMonth)){
			fundInfo.setPayYearMonth(payYearMonth);
		}
		fundInfo.setPayMonthCount(payMonthCount);
	}


}
